package com.baturu.vin.dfpv.service.impl;

import com.baturu.carvin.dtos.CarTypeDTO;
import com.baturu.carvin.service.CarTypeService;
import com.baturu.experimental.service.CarTypeQueryService;
import com.baturu.vin.base.dto.BasePartsImageInfoDTO;
import com.baturu.vin.base.service.impl.EPCAnalyser;
import com.baturu.vin.base.util.MD5Utils;
import com.baturu.vin.dfpv.dal.dao.*;
import com.baturu.vin.dfpv.dal.entity.DfpvPartsPostionQueryDTO;
import com.baturu.vin.dfpv.dto.*;
import com.baturu.vin.dto.CarTypeBuilderDTO;
import com.baturu.vin.dto.EPCAnalyserEnum;
import com.baturu.vin.dto.EPCCarTypeDTO;
import com.baturu.vin.dto.EPCPartsImageDTO;
import com.baturu.vin.service.EPCVinCacheService;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
@Service("dfpvEPCAnalyser")
public class DfpvEPCAnalyser extends EPCAnalyser<DfpvVinDTO, DfpvPartsMappingDTO, DfpvPartsInfoDTO, EPCCarTypeDTO> {

    @Autowired
    private DfpvVinDAO dfpvVinDAO;
    @Autowired
    private DfpvPartsDAO dfpvPartsDAO;
    @Autowired
    private DfpvPartsPositionDAO dfpvPartsPositionDAO;
    @Autowired
    private DfpvPartsMappingDAO dfpvPartsMappingDAO;
    @Autowired
    private DfpvCarSystemBtrIdDAO dfpvCarSystemBtrIdDAO;
    @Autowired
    private DfpvCarSystemDAO dfpvCarSystemDAO;
    @Autowired
    private CarTypeService carTypeService;


    @Autowired
    private CarTypeQueryService carTypeQueryService;
    @Autowired
    private EPCVinCacheService epcVinCacheService;

    @Override
    protected DfpvVinDTO analyseVinCode(String vinCode) {
        DfpvVinDTO vinModel = dfpvVinDAO.findVinModel(vinCode);
        if (Objects.isNull(vinModel)) {
            epcVinCacheService.addWaitCacheVinCode(EPCAnalyserEnum.DFPV, vinCode);
            return null;
        }
        return vinModel;
    }

    @Override
    protected List<EPCCarTypeDTO> queryCarTypes(DfpvVinDTO analyseResult) {
        DfpvCarSystemDTO carSystemBtrIdDTO = buildCarType(analyseResult);
        if (Objects.isNull(carSystemBtrIdDTO)) {
            return Collections.emptyList();
        }
        EPCCarTypeDTO epcCarTypeDTO = new EPCCarTypeDTO();
        epcCarTypeDTO.setBtrId(carSystemBtrIdDTO.getBtrId());
        return Lists.newArrayList(epcCarTypeDTO);
    }

    private DfpvCarSystemDTO buildCarType(DfpvVinDTO analyseResult) {
        if(Objects.isNull(analyseResult)) {
            return null;
        }
        String dfpvBtrId = getDfpvBtrId(analyseResult);
        DfpvCarSystemDTO dfpvCarSystemDTO = dfpvCarSystemDAO.queryCarSystemIdByBtrId(analyseResult.getCarSystem());
        dfpvCarSystemDTO.setBtrId(dfpvBtrId);
        if(Objects.isNull(dfpvCarSystemDTO)) {
            return null;
        }
        saveCarType(analyseResult,dfpvCarSystemDTO);
        return dfpvCarSystemDTO;
    }

    private int saveCarType(DfpvVinDTO analyseResult, DfpvCarSystemDTO dfpvCarSystemDTO) {
        String btrId = dfpvCarSystemDTO.getBtrId();
        if (StringUtils.isBlank(btrId)) {
            return 0;
        }
        List<CarTypeDTO> carTypeDTOs = carTypeService.findCarTypeByBtrIds(Lists.newArrayList(btrId));
        if (CollectionUtils.isNotEmpty(carTypeDTOs)) {
            return 0;
        }

        return epcVinCacheService.saveNewCarType(Lists.newArrayList(CarTypeBuilderDTO
                .builder()
                .btrId(btrId)
                .btrBrand(analyseResult.getBtrBrand())
                .btrSubBrand(analyseResult.getBtrSubBrand())
                .carSystemId(dfpvCarSystemDTO.getCarSystemId())
                .salesName(analyseResult.getDisplacement() + " " + analyseResult.getTransmissionType())
                .chassis(analyseResult.getChassis())
                .displacement(analyseResult.getDisplacement())
                .carSystem(analyseResult.getCarSystem())
                .productTime(analyseResult.getMj())
                .engineType(analyseResult.getEngineType())
                .transmissionType(analyseResult.getTransmissionType())
                .transmissionDesc(analyseResult.getTransmissionDesc())
                .btrCountry("中国")
                .build()));
    }

    private String getDfpvBtrId(DfpvVinDTO analyseResult) {
        List<DfpvCarSystemBtrIdDTO> dfpvCarSystemBtrIdDTOs = dfpvCarSystemBtrIdDAO.queryCarSystemByVinResult(analyseResult.getCarSystem(), analyseResult.getEngineType(), analyseResult.getTransmissionType());
        if(CollectionUtils.isNotEmpty(dfpvCarSystemBtrIdDTOs)) {
            return dfpvCarSystemBtrIdDTOs.get(0).getBtrId();
        }
        String year = analyseResult.getMj();
        String btrId ="DfpvEPC_"+ MD5Utils.getMD5Code(analyseResult.getBtrBrand()+analyseResult.getEngineType()+analyseResult.getEngineType()+analyseResult.getCarSystem()+analyseResult.getTransmissionType())+"_"+year;
        DfpvCarSystemBtrIdDTO dfpvCarSystemBtrIdDTO = DfpvCarSystemBtrIdDTO.builder()
                .btrId(btrId)
                .carSystem(analyseResult.getCarSystem())
                .engineType(analyseResult.getEngineType())
                .transmission(analyseResult.getTransmissionType())
                .year(analyseResult.getMj())
                .build();
        dfpvCarSystemBtrIdDAO.insertCarSystemBtrId(dfpvCarSystemBtrIdDTO);
        return btrId;
    }

    @Override
    protected List<DfpvPartsMappingDTO> queryPartsMappings(Set<String> btrPartsNames, DfpvVinDTO analyseResult) {
        return dfpvPartsMappingDAO.queryPartsMappingBySeriesAndBtrPartsName(analyseResult.getCarSystem(), btrPartsNames);
    }

    @Override
    protected List<DfpvPartsInfoDTO> queryPartsInfoByPartsMapping(List<DfpvPartsMappingDTO> partsMappings, DfpvVinDTO analyseResult) {
        Set<String> partsCodeList = partsMappings.stream()
                .map(DfpvPartsMappingDTO::getPartsCode)
                .collect(Collectors.toSet());

        return queryPartsInfoByPartsCode(partsCodeList, analyseResult);
    }

    @Override
    protected List<DfpvPartsInfoDTO> queryPartsInfoByPartsCode(Set<String> partsCodes) {
        return Collections.emptyList();
    }

    @Override
    protected Map<String, List<EPCPartsImageDTO>> queryPartsImage(Map<String, List<DfpvPartsInfoDTO>> partsInfoGroup, DfpvVinDTO analyseResult) {
        List<DfpvPartsPostionQueryDTO> queryParams = partsInfoGroup.values().stream()
                .flatMap(Collection::stream)
                .map(d -> DfpvPartsPostionQueryDTO.builder().imageCode(d.getGroupCode()).picOn(d.getPicOn()).series(d.getSeries()).build())
                .distinct().collect(Collectors.toList());
        if (CollectionUtils.isEmpty(queryParams)) {
            return Collections.emptyMap();
        }
       /* StreamKit.partitionStream(queryParams, SEGMENT_QUERY_SIZE)
                .flatMap(d -> dfpvPartsPositionDAO.findPartsPositions(d).stream())
                .collect(Collectors.toList());*/
        return super.queryPartsImage(partsInfoGroup, analyseResult);
    }

    @Override
    protected Map<String, List<BasePartsImageInfoDTO>> queryEPCImageInfo(Set<String> sortNames, DfpvVinDTO analyseResult) {
//        StreamKit.partitionStream(sortNames,SEGMENT_QUERY_SIZE).flatMap(list -> dfpvPartsPositionDAO.)
        return super.queryEPCImageInfo(sortNames, analyseResult);
    }

    @Override
    protected List<DfpvPartsInfoDTO> queryPartsInfoByPartsCode(Set<String> partsCodes, DfpvVinDTO analyseResult) {
        return dfpvPartsDAO.queryPartsBySeries(analyseResult.getCarSystem(), partsCodes);
    }

    @Override
    protected List<DfpvPartsInfoDTO> filterPartsInfoByVinAnalyseResult(List<DfpvPartsInfoDTO> partsInfo, DfpvVinDTO analyseResult) {

        List<DfpvPartsInfoDTO> haveGroupDes = partsInfo.stream().filter(d -> StringUtils.isNotEmpty(d.getGroupDes())).collect(Collectors.toList());
        if (StringUtils.isNotEmpty(analyseResult.getEngineType())) {
            haveGroupDes.removeIf(d -> hasEngineType(d.getGroupDes(), analyseResult));
        }
        haveGroupDes.removeIf(d -> hasVinOrTransmissionType(d.getGroupDes(), analyseResult));
        List<DfpvPartsInfoDTO> haveSys = haveGroupDes.stream()
                .filter(d -> StringUtils.contains(d.getGroupDes(), analyseResult.getCarSystem()))
                .collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(haveSys)) {
            haveGroupDes = haveSys;
        }

        if(haveGroupDes.stream().anyMatch(d -> StringUtils.contains(d.getGroupDes(),"CROSS"))) {
            haveGroupDes.removeIf(d -> StringUtils.equals(d.getSeries(), "H30") && !matchCross(d.getGroupDes(), analyseResult.getCarType()));
        }
        partsInfo = partsInfo.stream().filter(d -> StringUtils.isBlank(d.getGroupDes())).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(haveGroupDes)) {
            partsInfo.addAll(haveGroupDes);
        }

        String vinYear = analyseResult.getMj();
        if (StringUtils.isNotBlank(vinYear)) {
            partsInfo.removeIf(d -> StringUtils.substringBefore(d.getEntryTime(), "-").compareTo(vinYear) > 0);
        }

        List<DfpvPartsInfoDTO> haveVin = partsInfo.stream().filter(d -> StringUtils.isNotBlank(d.getVin())).collect(Collectors.toList());
        haveVin.removeIf(d -> matchVin(d.getVin(), analyseResult.getVinCode()));
        partsInfo = partsInfo.stream().filter(d -> StringUtils.isBlank(d.getVin())).collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(haveVin)) {
            partsInfo.addAll(haveVin);
        }
        return partsInfo;
    }

    private boolean matchCross(String groupDes, String carType) {
        if(StringUtils.contains(carType,"CROSS")){
            return StringUtils.contains(groupDes,"CROSS");
        }
        return false;
    }

    private boolean matchVin(String vin, String vinCode) {
        vin = vin.replaceAll("；", "，");
        List<String> vinList = Splitter.on("，").splitToList(vin).stream()
                .filter(d -> StringUtils.isNotBlank(d)).collect(Collectors.toList());
        String vinAfter6 = StringUtils.substring(vinCode, 11, 17);
        boolean correct = true;

        for (String getVin : vinList) {
            if (StringUtils.length(getVin) == 17) {
                correct = match17Vin(getVin, vinAfter6);
            }else {
                correct = matchLess17Vin(getVin,vinAfter6);
            }
            if (!correct) {
                return correct;
            }
        }
        return correct;
    }

    private boolean matchLess17Vin(String getVin,String vinAfter6) {
        if(StringUtils.contains(getVin,"=")){
            return get6Number(getVin).compareTo(vinAfter6) == 0;
        }else {
            return compareVinOnLeft(getVin,vinAfter6);
        }

    }

    private boolean match17Vin(String getVin, String vinAfter6) {
        List<String> vinWith2 = Splitter.on("VIN").splitToList(getVin);
        Boolean firstBool = true;
        Boolean secondBool = true;
        if (vinWith2.size() == 2) {
            String first = vinWith2.get(0);
            String second = vinWith2.get(1);
            firstBool = compareVinOnRight(first, vinAfter6);
            secondBool = compareVinOnLeft(second, vinAfter6);
        }
        return firstBool || secondBool;
    }

    private boolean compareVinOnLeft(String second, String vinAfter6) {
        String secondNumber = get6Number(second);
        if (StringUtils.contains(second, "≤")) {
            return vinAfter6.compareTo(secondNumber) > 0;
        } else if (StringUtils.containsAny(second, "＜", "〈")) {
            return vinAfter6.compareTo(secondNumber) >= 0;
        } else if (StringUtils.contains(second, "≥")) {
            return vinAfter6.compareTo(secondNumber) < 0;
        } else if (StringUtils.contains(second, "＞")) {
            return vinAfter6.compareTo(secondNumber) <= 0;
        }
        return true;
    }

    private boolean compareVinOnRight(String first, String vinAfter6) {
        String firstNumber = get6Number(first);
        if (StringUtils.contains(first, "≤")) {
            return firstNumber.compareTo(vinAfter6) > 0;
        } else if (StringUtils.containsAny(first, "＜", "〈")) {
            return firstNumber.compareTo(vinAfter6) >= 0;
        } else if (StringUtils.contains(first, "≥")) {
            return firstNumber.compareTo(vinAfter6) < 0;
        } else if (StringUtils.contains(first, "＞")) {
            return firstNumber.compareTo(vinAfter6) <= 0;
        }
        return true;
    }

    private String get6Number(String vin) {
        return Pattern.compile("[^0-9]").matcher(vin).replaceAll("").trim();
    }

    private boolean hasEngineType(String groupDes, DfpvVinDTO analyseResult) {
        String engineType = analyseResult.getEngineType();
        String displacement = analyseResult.getDisplacement();
        if (StringUtils.contains(groupDes, analyseResult.getEngineType())) {
            String rexDisplacement = "^[\\s\\S]*" + engineType + " \\d\\.\\d[TL][\\s\\S]*$";
            Pattern displacementPat = Pattern.compile(rexDisplacement);
            if (displacementPat.matcher(groupDes).matches()) {
                String e = Splitter.on(engineType).splitToList(groupDes).get(1);
                String displacement4get = StringUtils.substring(e, 1, 5);
                return !StringUtils.equalsIgnoreCase(displacement, displacement4get);
            } else {
                return false;
            }
        }
        return true;
    }

    private boolean hasVinOrTransmissionType(String groupDes, DfpvVinDTO analyseResult) {
        Boolean vinBool = false;
        Boolean transmissionBool = false;
        String vinRex = "^[\\s\\S]*[≥＜]\\d{6}[\\s\\S]*$";
        Pattern vin6 = Pattern.compile(vinRex);
        if (vin6.matcher(groupDes).matches()) {
            List<String> list = Splitter.on("≥").splitToList(groupDes);
            String vinAfter6 = StringUtils.substring(analyseResult.getVinCode(), 11, 17);
            if (list.size() < 2) {
                list = Splitter.on("＜").splitToList(groupDes);
            }
            String vin = list.get(1).substring(0, 6);
            vinBool = compareVinOnLeft(vin,vinAfter6);
        }

        if (StringUtils.containsAny(groupDes, "AT", "MT")) {
            transmissionBool = !StringUtils.containsAny(groupDes, analyseResult.getTransmissionType());
        }
        return vinBool || transmissionBool;
    }

    @Override
    protected List<DfpvPartsMappingDTO> queryPartsMappingByPartsInfoAndAnalyseResult(Map<String, List<DfpvPartsInfoDTO>> partsInfoGroup, DfpvVinDTO analyseResult) {
        if (Objects.isNull(analyseResult)) {
            return Collections.emptyList();
        }
        Set<String> partsCodes = partsInfoGroup.values().stream()
                .flatMap(l -> l.stream().map(DfpvPartsInfoDTO::getPartsCode))
                .collect(Collectors.toSet());
        return dfpvPartsMappingDAO.queryPartsMappingBySeriesAndPartsCode(analyseResult.getCarSystem(), partsCodes);
    }

    @Override
    protected Map<String, String> queryEpcPartsName(Map<String, List<DfpvPartsInfoDTO>> partsInfoGroup, DfpvVinDTO analyseResult) {
        return partsInfoGroup.values().stream()
                .flatMap(Collection::stream)
                .collect(Collectors.toMap(DfpvPartsInfoDTO::getPartsCode, DfpvPartsInfoDTO::getSparepName, (v1, v2) -> v1));
    }
}
